package com.brzjomo.embraceofthevoid.items;

import com.brzjomo.embraceofthevoid.EmbraceOfTheVoid;
import com.brzjomo.embraceofthevoid.init.ModItems;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;

public class NightFallTeleportGem extends Item {
    public static int dayLength = 24000;
    public long targetTime = 12000;
    public long realTime;

    public NightFallTeleportGem(Settings settings) {
        super(settings);
    }

    // Time related code
    public long getDay(long time) {
        return time / dayLength;
    }

    public long getDayPlusOne(long time) {
        return time / dayLength + 1;
    }

    public long getDayTime(long time) {
        return time % dayLength;
    }

    public String getDayTimeInClock(long time) {
        double tickPerHour = 1000;
        double tickPerMinute = tickPerHour / 60;

        double hour = Math.floor(getDayTime(time) / tickPerHour);
        double minute = Math.floor((getDayTime(time) - tickPerHour * hour) / tickPerMinute);

        if (hour < 18) {
            hour += 6;
        } else {
            hour -= 18;
        }

        int trimHour = (int) hour;
        int trimMinute = (int) minute;

        return formatNumForClock(trimHour) + ":" + formatNumForClock(trimMinute);
    }

    public String formatNumForClock(int num) {
        if (num < 10) {
            return "0" + num;
        } else {
            return "" + num;
        }
    }

    public String printCurrentTime(long time) {
        return "第 " + getDayPlusOne(time) + " 天，" + getDayTimeInClock(time) + " (24小时制)";
    }

    // Name related code
    @Override
    public Text getName() {
        return Text.translatable(this.getTranslationKey(), getDayTimeInClock(realTime));
    }

    @Override
    public Text getName(ItemStack stack) {
        return Text.translatable(this.getTranslationKey(stack), getDayTimeInClock(realTime));
    }

    // NBT related code
    public void writePositionToNbt(NbtCompound nbt, BlockPos pos) {
        nbt.putInt("PinnedPos_X", pos.getX());
        nbt.putInt("PinnedPos_Y", pos.getY());
        nbt.putInt("PinnedPos_Z", pos.getZ());
    }

    public BlockPos readPositionFromNbt(NbtCompound nbt) {
        return new BlockPos(nbt.getInt("PinnedPos_X"), nbt.getInt("PinnedPos_Y"), nbt.getInt("PinnedPos_Z"));
    }

    public String printPositionFromNbt(NbtCompound nbt) {
        return "X: " + nbt.getInt("PinnedPos_X") + " , Y: " + nbt.getInt("PinnedPos_Y") + " , Z: " + nbt.getInt("PinnedPos_Z");
    }

    public String printNBT(NbtCompound nbt) {
        return nbt.toString();
    }

    public void writeWorldRegistryToNbt(NbtCompound nbt, World world) {
        nbt.putString("PinnedWorldRegistry", world.getRegistryKey().toString());
    }

    public Boolean isSameWorld(NbtCompound nbt, PlayerEntity player) {
        return player.getWorld().getRegistryKey().toString().equals(nbt.getString("PinnedWorldRegistry"));
    }

    public void logPosition(BlockPos pos) {
        String output = "X: " + pos.getX() + " , Y: " + pos.getY() + " , Z: " + pos.getZ();
        EmbraceOfTheVoid.LOGGER.info(output);
    }

    public void logPosition(double X, double Y, double Z) {
        String output = "X: " + X + " , Y: " + Y + " , Z: " + Z;
        EmbraceOfTheVoid.LOGGER.info(output);
    }

    public String getWorldID(String worldRegistryKey) {
        Pattern rule = Pattern.compile(":");
        String[] id = rule.split(worldRegistryKey);
        return id[id.length - 1].substring(0, id[id.length - 1].length() - 1);
    }

    public String getWorldNameSpace(String worldRegistryKey) {
        Pattern rule = Pattern.compile(":");
        String[] id = rule.split(worldRegistryKey);

        Pattern rule2 = Pattern.compile(" ");
        id = rule2.split(id[id.length - 2]);
        return id[id.length - 1];
    }

    public String getPlayerWorldInfo(PlayerEntity player) {
        String currentDimensionRegistryKey = player.getWorld().getRegistryKey().toString();

        return this.getWorldNameSpace(currentDimensionRegistryKey) + ":" + this.getWorldID(currentDimensionRegistryKey);
    }

    public String getDestinationWorldInfo(NbtCompound nbt) {
        String pinnedDimensionRegistryKey = nbt.getString("PinnedWorldRegistry");

        return this.getWorldNameSpace(pinnedDimensionRegistryKey) + ":" + this.getWorldID(pinnedDimensionRegistryKey);
    }

    public String getDestinationWorldInfo(NbtCompound nbt, PlayerEntity player) {
        String pinnedDimensionRegistryKey = nbt.getString("PinnedWorldRegistry");
        if (Objects.equals(pinnedDimensionRegistryKey, "")) {
            return this.getPlayerWorldInfo(player);
        }

        return this.getWorldNameSpace(pinnedDimensionRegistryKey) + ":" + this.getWorldID(pinnedDimensionRegistryKey);
    }

    public void logPlayerFromAndGo(PlayerEntity player, double X2, double Y2, double Z2) {
        double X1 = player.getSteppingPos().getX();
        double Y1 = player.getSteppingPos().getY();
        double Z1 = player.getSteppingPos().getZ();
        String output = "EOTV: " + player.getName().getString() + "(" + player.getUuidAsString() + ")" + " teleport from (" + X1 + "," + Y1 + "," + Z1 + ") to (" + X2 + "," + Y2 + "," + Z2 + ")";
        EmbraceOfTheVoid.LOGGER.info(output);
    }

    public void logPlayerFromAndGo(PlayerEntity player, double X2, double Y2, double Z2, String startDim, String destinationDim) {
        double X1 = player.getSteppingPos().getX();
        double Y1 = player.getSteppingPos().getY();
        double Z1 = player.getSteppingPos().getZ();
        String output = "EOTV: " + player.getName().getString() + "(" + player.getUuidAsString() + ")" + " teleport from [" + startDim + "](" + X1 + "," + Y1 + "," + Z1 + ") to [" + destinationDim + "](" + X2 + "," + Y2 + "," + Z2 + ")";
        EmbraceOfTheVoid.LOGGER.info(output);
    }


    // Mark position
    @Override
    public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
        ItemStack itemStack = user.getStackInHand(hand);
        NbtCompound nbt = itemStack.getOrCreateNbt();
        int maxDamage = itemStack.getMaxDamage();

        var damageLimit = maxDamage - 1;

        if (user.isSneaking()) {
            if (world.isClient()) {
                if (!nbt.contains("Damage") || nbt.getInt("Damage") < damageLimit) {
                    user.playSound(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.PLAYERS, 0.8F, 0.4F);
                }
            }

            if (!world.isClient()) {
                if (!nbt.contains("Damage") || nbt.getInt("Damage") < damageLimit) {
                    writePositionToNbt(nbt, user.getBlockPos());
                    writeWorldRegistryToNbt(nbt, user.getWorld());

                    //damage item
                    itemStack.damage(1, user, (p) -> p.sendToolBreakStatus(user.getActiveHand()));

                    // set cool down
                    user.getItemCooldownManager().set(this, 100);

                    return TypedActionResult.success(itemStack);
                } else {
                    return TypedActionResult.fail(itemStack);
                }
            }
        } else {
            if (!world.isClient()) {
                return TypedActionResult.pass(itemStack);
            }
        }
        return TypedActionResult.pass(itemStack);
    }

    // Teleport related code
    @Override
    public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {
        realTime = world.getTimeOfDay();
        NbtCompound nbt = stack.getOrCreateNbt();

        // Restore Durability
        if (stack.isOf(ModItems.NIGHTFALL_TELEPORT_GEM)) {
            if (!nbt.contains("tickCount")) {
                nbt.putInt("tickCount", 0);
            } else {
                nbt.putInt("tickCount", nbt.getInt("tickCount") + 1);

                if (nbt.getInt("tickCount") > dayLength) {
                    if (nbt.getInt("Damage") > 0) {
                        nbt.putInt("Damage", nbt.getInt("Damage") - 1);
                    }

                    nbt.putInt("tickCount", 0);
                }
            }
        }

        if (stack.isOf(ModItems.NIGHTFALL_TELEPORT_GEM)) {
            if (!nbt.contains("particleTickCount")) {
                nbt.putInt("particleTickCount", 35);
            } else {
                nbt.putInt("particleTickCount", nbt.getInt("particleTickCount") + 1);

                if (nbt.getInt("particleTickCount") > 100) {
                    nbt.putInt("particleTickCount", 35);
                }
            }
        }

        if (entity.isPlayer()) {
            PlayerEntity player = (PlayerEntity) entity;

            // Notify player
            if (getDayTime(realTime) == getDayTime(targetTime) - 20 * 30) {
                player.sendMessage(Text.translatable("message." + EmbraceOfTheVoid.MOD_ID + ".near_dusk").formatted(Formatting.GOLD), true);
            }

            if (getDayTime(realTime) == getDayTime(targetTime) && !player.getItemCooldownManager().isCoolingDown(this)) {
                // decide which gem should active
                if (stack.isOf(ModItems.NIGHTFALL_TELEPORT_GEM)) {
                    ItemStack mainHandStack = player.getMainHandStack();
                    ItemStack offHandStack = player.getOffHandStack();
                    NbtCompound mainHandNBT = mainHandStack.getOrCreateNbt();
                    NbtCompound offHandNBT = offHandStack.getOrCreateNbt();

                    boolean ifGemInMainHandValid = selected && mainHandNBT.contains("PinnedPos_X");
                    boolean ifGemInOffHandValid = offHandStack.isOf(ModItems.NIGHTFALL_TELEPORT_GEM) && offHandNBT.contains("PinnedPos_X");
                    boolean ifGemInMainHandUnValid = !mainHandStack.isOf(ModItems.NIGHTFALL_TELEPORT_GEM) || (mainHandStack.isOf(ModItems.NIGHTFALL_TELEPORT_GEM) && !mainHandNBT.contains("PinnedPos_X"));

                    // 优先主手，筛选主手宝石
                    if (ifGemInMainHandValid) {
                        mainHandNBT.putBoolean("shouldActive", true);
                    } else if (ifGemInOffHandValid && ifGemInMainHandUnValid) {
                        offHandNBT.putBoolean("shouldActive", true);
                    }
                }
            }

            // active & teleport
            if (nbt.getBoolean("shouldActive")) {
                // server: teleport player
                if (!world.isClient()) {
                    // in same world
                    if (isSameWorld(nbt, player)) {
                        BlockPos pinnedPosition = readPositionFromNbt(nbt);
                        this.logPlayerFromAndGo(player, pinnedPosition.getX() + 0.5D, pinnedPosition.getY() + 1.0D, pinnedPosition.getZ() + 0.5D, this.getPlayerWorldInfo(player), this.getDestinationWorldInfo(nbt));
                        player.requestTeleport(pinnedPosition.getX() + 0.5D, pinnedPosition.getY() + 1.0D, pinnedPosition.getZ() + 0.5D);
                    }

                    // in different worlds
                    if (!isSameWorld(nbt, player)) {
                        String pinnedDimension = nbt.getString("PinnedWorldRegistry");
                        Identifier dimensionID = new Identifier(this.getWorldNameSpace(pinnedDimension), this.getWorldID(pinnedDimension));

                        RegistryKey<World> worldRegistryKey = RegistryKey.of(RegistryKeys.WORLD, dimensionID);
                        ServerWorld serverWorld = Objects.requireNonNull(world.getServer()).getWorld(worldRegistryKey);

                        if (player.lastNetherPortalPosition == null) {
                            player.lastNetherPortalPosition = new BlockPos(0, 0, 0);
                        }

                        BlockPos pinnedPosition = readPositionFromNbt(nbt);
                        this.logPlayerFromAndGo(player, pinnedPosition.getX() + 0.5D, pinnedPosition.getY() + 1.0D, pinnedPosition.getZ() + 0.5D, this.getPlayerWorldInfo(player), this.getDestinationWorldInfo(nbt));
                        player.moveToWorld(serverWorld);
                        player.requestTeleport(pinnedPosition.getX() + 0.5D, pinnedPosition.getY() + 1.0D, pinnedPosition.getZ() + 0.5D);
                    }

                    // set cool down
                    player.getItemCooldownManager().set(this, 100);

                    // damage and decrease item
                    int maxDamage = stack.getMaxDamage();
                    if (nbt.getBoolean("shouldActive")) {
                        nbt.putInt("Damage", nbt.getInt("Damage") + 1);

                        if (nbt.getInt("Damage") >= maxDamage) {
                            stack.decrement(1);
                        }
                    }

                    // update NBT
                    nbt.putBoolean("shouldActive", false);
                }

                // broadcast sound
                world.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.PLAYERS, 1.0F, 0.4F);

                // trigger particle
                nbt.putInt("particleTickCount", 0);
            }
        }

        // add particle
        if (nbt.getInt("particleTickCount") < 35) {
            world.addParticle(ParticleTypes.PORTAL, entity.getX(), entity.getBodyY(0.5D), entity.getZ(), (Random.create().nextDouble() - 0.5D) * 2, -Random.create().nextDouble(), (Random.create().nextDouble() - 0.5D) * 2);
        }

        super.inventoryTick(stack, world, entity, slot, selected);
    }

    // tooltips
    @Override
    public void appendTooltip(ItemStack stack, @Nullable World world, List<Text> tooltip, TooltipContext context) {
        // nbt
        NbtCompound nbt = stack.getOrCreateNbt();

        if (stack.isOf(ModItems.NIGHTFALL_TELEPORT_GEM)) {
            tooltip.add(Text.translatable(stack.getTranslationKey() + ".currentTime", getDayTimeInClock(realTime)).formatted(Formatting.WHITE));
            tooltip.add(Text.translatable(stack.getTranslationKey() + ".targetTime", getDayTimeInClock(targetTime)).formatted(Formatting.YELLOW));
        }

        // show recorded position if exist
        if (nbt.contains("PinnedPos_X")) {
            tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".teleport_gem" + ".recordedPosition", printPositionFromNbt(nbt)).formatted(Formatting.AQUA));
        } else {
            tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".teleport_gem" + ".noRecord").formatted(Formatting.DARK_GRAY));
        }

        // show recorded dimension if exist
        if (nbt.contains("PinnedWorldRegistry")) {
            String pinnedDimension = nbt.getString("PinnedWorldRegistry");

            // extract id
            Pattern rule = Pattern.compile(":");
            String[] id = rule.split(pinnedDimension);
            pinnedDimension = id[id.length - 1].substring(0, id[id.length - 1].length() - 1);

            // extract namespace
            String dimensionNameSpace;
            Pattern rule2 = Pattern.compile(" ");
            id = rule2.split(id[id.length - 2]);
            dimensionNameSpace = id[id.length - 1];

            var textOverWorld = Text.translatable("dimension." + EmbraceOfTheVoid.MOD_ID + ".overworld");
            var textTheNether = Text.translatable("dimension." + EmbraceOfTheVoid.MOD_ID + ".the_nether");
            var textTheEnd = Text.translatable("dimension." + EmbraceOfTheVoid.MOD_ID + ".the_end");

            if (!EmbraceOfTheVoid.IS_DEBUG) {
                if (dimensionNameSpace.equals("minecraft")) {
                    switch (pinnedDimension) {
                        case "overworld" ->
                                tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".teleport_gem" + ".recordedDimension", textOverWorld).formatted(Formatting.DARK_GREEN));
                        case "the_nether" ->
                                tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".teleport_gem" + ".recordedDimension", textTheNether).formatted(Formatting.DARK_GREEN));
                        case "the_end" ->
                                tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".teleport_gem" + ".recordedDimension", textTheEnd).formatted(Formatting.DARK_GREEN));
                    }
                } else {
                    pinnedDimension = dimensionNameSpace + " : " + pinnedDimension;
                    tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".teleport_gem" + ".recordedDimension", pinnedDimension).formatted(Formatting.DARK_GREEN));
                }
            } else {
                pinnedDimension = dimensionNameSpace + " : " + pinnedDimension;
                tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".teleport_gem" + ".recordedDimension", pinnedDimension).formatted(Formatting.DARK_GREEN));
            }
        }

        // show NBT
        if (EmbraceOfTheVoid.IS_DEBUG) {
            tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".teleport_gem" + ".NBT", nbt.toString()).formatted(Formatting.LIGHT_PURPLE));
        }

        // show gem description
        if (!EmbraceOfTheVoid.IS_DEBUG) {
            if (stack.isOf(ModItems.NIGHTFALL_TELEPORT_GEM)) {
                tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".nightfall_teleport_gem" + ".description", nbt.toString()).formatted(Formatting.WHITE));
                tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".nightfall_teleport_gem" + ".description" + ".2", nbt.toString()).formatted(Formatting.ITALIC).formatted(Formatting.DARK_GRAY));
            } else if (stack.isOf(ModItems.BLESS_TELEPORT_GEM)) {
                tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".bless_teleport_gem" + ".description", nbt.toString()).formatted(Formatting.WHITE));
                tooltip.add(Text.translatable("item." + EmbraceOfTheVoid.MOD_ID + ".bless_teleport_gem" + ".description" + ".2", nbt.toString()).formatted(Formatting.ITALIC).formatted(Formatting.DARK_GRAY));
            }
        }

        super.appendTooltip(stack, world, tooltip, context);
    }
}
